#include "keyboard.h"
#include <windows.h>
#include "dxManager.h"
#include <dinput.h>
#include "game.h"
#include "camera.h"

#define GRIDSIZE 150
#define GRIDSPACE 0.4f

// Initialization phase
Game::Game()
{

}

void Game::Init(D3DXMATRIX* viewMat)
{
	initDirectInput();
	initMeshes();
	
	//create the font for the HUD
	D3DXCreateFont(pd3dDevice, 20, 0, FW_BOLD, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Arial"), &m_font);
	
	//text color for "HUD"
	fontColor = D3DCOLOR_ARGB(255,0,100,0);

	//set up HUD rect
	rct.left = 2;
	rct.right = 150;
	rct.top = 10;
	rct.bottom = rct.top+120;


	gameInit();
	audio = new Audio(this);
	audio->Init();
	
	camera.setPosition(&D3DXVECTOR3(0.0f, 2.0f, -5.0f));

	// Initialize all the entities to "dead"
	for (int i = 0; i < NUM_ENTITIES; i++)
		entList[i] = new Entity();

	createEntity(ENT_PLAYER, box, mtrl, 0, 1, 0);
	red = 1;
	createEntity(ENT_LIGHT, world, mtrl, 0, 2, 2);
	red = 0;
	green = 1;
	createEntity(ENT_LIGHT, box, mtrl, 5, 1, 0);
	green = 0;
	blue = 1;
	createEntity(ENT_LIGHT, box, mtrl, -5, 1, 0);
	
	this->matView = viewMat;
}

void Game::gameInit()
{
	keyboard = new Keyboard(*hInst, wndHandle);
	SetupVB();
	numLights = 0;
	rave = false;
	red = green = blue = 0;
}

void Game::Update()
{
	float camSpeed = 0.5f;
	float aimSpeed = 0.1f;

	// Ping the keyboard for the current keys pressed
	keyboard->pingKeyboard();

	// Make the camera work
	camera.getViewMatrix(matView);

	// Camera controls
	// Movement
	if(keyboard->isKeyHeld(KEY_A))
		camera.strafe(-camSpeed);
	else if(keyboard->isKeyHeld(KEY_D))
		camera.strafe(camSpeed);
	if(keyboard->isKeyHeld(KEY_W))
		camera.walk(camSpeed);
	else if(keyboard->isKeyHeld(KEY_S))
		camera.walk(-camSpeed);
	
	// Aiming
	if(keyboard->isKeyHeld(KEY_J))
		camera.yaw(-aimSpeed);
	else if(keyboard->isKeyHeld(KEY_L))
		camera.yaw(aimSpeed);
	if(keyboard->isKeyHeld(KEY_I))
		camera.pitch(-aimSpeed);
	else if(keyboard->isKeyHeld(KEY_K))
		camera.pitch(aimSpeed);

	audio->update();
	
	// Update the entities
	for (int i = 0; i < NUM_ENTITIES; i++)
	{
		entList[i]->Update();
	}

	if(rave)
		pd3dDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(0, 0, 0));
	else
		pd3dDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(SKY_R, SKY_G , SKY_B));
}

bool Game::createEntity(ent_type type, ID3DXMesh *mesh, D3DMATERIAL9 material, float x, float y, float z)
{
	int i;

	for(i = 0; i < NUM_ENTITIES; i++)
	{
		if(!entList[i]->isAlive)
			break;
	}

	// Couldn't create entity, out of space!
	if(entList[i]->isAlive && i == NUM_ENTITIES-1)
		return false;

	switch(type)
	{
		case ENT_PLAYER:
			entList[i] = new Player(this);
			break;
		case ENT_LIGHT:
			Light* tempLight;
			entList[i] = new Light(this);
			tempLight = (Light*)entList[i];
			tempLight->initLight(red,green,blue);	
			break;
		case ENT_ENEMY:
			entList[i] = new Enemy(this);
			break;

	}

	entList[i]->setMesh(mesh);
	entList[i]->setMtrl(material);
	entList[i]->setX(x);
	entList[i]->setY(y);
	entList[i]->setZ(z);
	entList[i]->type = type;
	entList[i]->isAlive = true;

	return true;
}

void Game::Draw()
{
	pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 );
	pd3dDevice->BeginScene();

	// Do rendering here
	DrawWorld();

	// Draw entities
	for (int i = 0; i < NUM_ENTITIES; i++)
		entList[i]->draw();

	//draw some text
	if(!rave)
		m_font->DrawText(NULL, "Press 'R' for rave attack!", -1, &rct, DT_NOCLIP, fontColor); //DT_WORDBREAK DT_CALCRECT DT_NOCLIP

	pd3dDevice->EndScene();
	pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

void Game::DrawWorld()
{
	D3DXMATRIX aaa;
	D3DXMATRIX scale;
	D3DXMatrixIdentity(&aaa);
	D3DXMatrixScaling(&scale, 30.0f, 30.0f, 30.0f);
	
	// draw the contents of the vertex buffer
	drawVB(g_pVB);
	
	D3DXMatrixMultiply(&aaa, &aaa, &scale);
	D3DXMatrixMultiply(&aaa, &aaa, matView);

	pd3dDevice->SetTransform(D3DTS_WORLD, &aaa);
	
	pd3dDevice->SetMaterial(&skyMtrl);
	
	if(!rave)
		world->DrawSubset(0);
}

void Game::initMeshes()
{
	// Empty vertex buffer
	g_pVB = NULL;

	// Materials
	ZeroMemory(&mtrl, sizeof(D3DMATERIAL9));
	mtrl.Diffuse.r = mtrl.Diffuse.g = mtrl.Diffuse.b = 0.5f;
	mtrl.Specular.r = mtrl.Specular.g = mtrl.Specular.b = 1.0f;
	mtrl.Ambient.r = mtrl.Ambient.g = mtrl.Ambient.b = 0.2f;
	mtrl.Power = 8.0f;

	ZeroMemory(&skyMtrl, sizeof(D3DMATERIAL9));
	skyMtrl.Diffuse.r = skyMtrl.Diffuse.g = skyMtrl.Diffuse.b = 1.0f;
	skyMtrl.Specular.r = skyMtrl.Specular.g = skyMtrl.Specular.b = 1.0f;
	skyMtrl.Ambient.r = skyMtrl.Ambient.g = skyMtrl.Ambient.b = 1.0f;
	skyMtrl.Power = 8.0f;

	// Meshes
	D3DXCreateSphere(pd3dDevice, 1.0f, 15, 15, &world, NULL);
	D3DXCreateBox(pd3dDevice, 1.0f, 1.0f, 1.0f, &box, NULL);
}

///////////////////////// Stuff we shouldn't have to mess with ///////////////////////
void Game::initDirectInput()
{
	// Create the DirectInput object. 
	hr = DirectInput8Create(*hInst, DIRECTINPUT_VERSION, 
                            IID_IDirectInput8, (void**)&g_lpDI, NULL);

	// Retrieve a pointer to an IDirectInputDevice8 interface 
    hr = g_lpDI->CreateDevice(GUID_SysKeyboard, &g_lpDIDevice, NULL); 

	hr = g_lpDIDevice->SetDataFormat(&c_dfDIKeyboard);

	// Set the cooperative level 
    hr = g_lpDIDevice->SetCooperativeLevel(wndHandle, 
                             DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);

	// Get access to the input device. 
    hr = g_lpDIDevice->Acquire();
}

void Game::shutDown()
{
	if (g_lpDI) 
    { 
        if (g_lpDIDevice) 
        { 
        // Always unacquire device before calling Release(). 
            g_lpDIDevice->Unacquire(); 
            g_lpDIDevice->Release();
            g_lpDIDevice = NULL; 
        } 
        g_lpDI->Release();
        g_lpDI = NULL; 
    }
}

void Game::drawVB(LPDIRECT3DVERTEXBUFFER9 buffer)
{
	D3DXMATRIX matrix;
	D3DXMatrixIdentity(&matrix);

	D3DXMatrixMultiply(&matrix, &matrix, getView());

	pd3dDevice->SetStreamSource( 0, buffer, 0, sizeof(CUSTOMVERTEX) );
    pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
	pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
	pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	
	pd3dDevice->SetTransform(D3DTS_WORLD, &matrix);
	pd3dDevice->SetMaterial(&mtrl);
	
	for (int x=0; x<GRIDSIZE;x++)
		pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, x*GRIDSIZE*2, 2*GRIDSIZE-2);
	
	
}

LPDIRECT3DVERTEXBUFFER9 Game::createVertexBuffer(int size, DWORD usage)
{
	HRESULT hr;
	LPDIRECT3DVERTEXBUFFER9 buffer;

    // Create the vertex buffer.
    hr = pd3dDevice->CreateVertexBuffer( size,
                                         0, 
										 usage,
                                         D3DPOOL_DEFAULT, 
										 &buffer, 
										 NULL );
	if FAILED ( hr)
        return NULL;
    
	return buffer;
}

HRESULT Game::SetupVB()
{
	HRESULT hr;

	CUSTOMVERTEX g_Vertices[GRIDSIZE * GRIDSIZE * 2];
	int counter = 0;
	for(int y=-GRIDSIZE/2; y<GRIDSIZE/2; y++)
	{
		for (int x=-GRIDSIZE/2; x<GRIDSIZE/2; x++)
		{
			g_Vertices[counter].x = x * GRIDSPACE;
			g_Vertices[counter].y = 0.0;
			g_Vertices[counter].z = y * GRIDSPACE;
			g_Vertices[counter].color = D3DCOLOR_ARGB(255,255,255,255);
			counter++;

			g_Vertices[counter].x = x * GRIDSPACE;
			g_Vertices[counter].y = 0.0;
			g_Vertices[counter].z = y * GRIDSPACE + GRIDSPACE;
			g_Vertices[counter].color = D3DCOLOR_ARGB(255,255,255,255);
			counter++;
		}
	
	}

	// Create the vertex buffer
	g_pVB = createVertexBuffer(sizeof(g_Vertices)*sizeof(CUSTOMVERTEX), D3DFVF_CUSTOMVERTEX);

    // Fill the vertex buffer.
    VOID* pVertices;
	
	hr = g_pVB->Lock( 0, sizeof(g_Vertices), (void**)&pVertices, 0 );
	if FAILED (hr)
        return E_FAIL;

	// copy the vertices into the buffer
    memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );

	// unlock the vertex buffer
    g_pVB->Unlock();

    return S_OK;
}